package com.springboot.demo.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.StrUtil;
import com.cx.item.common.constants.ResultCode;
import com.cx.item.common.exception.ParamsCheckException;
import com.cx.item.common.service.impl.BaseServiceImpl;
import com.cx.item.common.utils.RegexUtil;
import com.springboot.demo.mapper.CustomerAddressMapper;
import com.springboot.demo.mapper.CustomerMapper;
import com.springboot.demo.model.constants.SunMiniappRedisConstants;
import com.springboot.demo.model.example.CustomerAddressExample;
import com.springboot.demo.model.example.CustomerExample;
import com.springboot.demo.model.model.Customer;
import com.springboot.demo.model.model.CustomerAddress;
import com.springboot.demo.model.par.AddressAddOrUpdatePar;
import com.springboot.demo.model.par.UpdateCustomerPar;
import com.springboot.demo.model.vo.AddressDetailsVo;
import com.springboot.demo.model.vo.AddressListVo;
import com.springboot.demo.model.vo.CustomerDetailsVo;
import com.springboot.demo.service.CustomerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Created by hwm on 2018/6/22.
 */
@Slf4j
@Service
public class CustomerServiceImpl extends BaseServiceImpl<Customer, CustomerExample, CustomerMapper> implements CustomerService {

    @Autowired
    private CustomerMapper customerMapper;
    @Autowired
    private CustomerAddressMapper customerAddressMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    @Transactional
    public int updateCustomer(UpdateCustomerPar updateCustomerPar) {

        Customer customerUp = new Customer();
        customerUp.setCustomerId(updateCustomerPar.getCustomerId());
        if (StrUtil.isNotEmpty(updateCustomerPar.getHeadImg())) {
            customerUp.setHeadImg(updateCustomerPar.getHeadImg());
        }
        if (StrUtil.isNotEmpty(updateCustomerPar.getName())) {
            customerUp.setName(updateCustomerPar.getName());
        }
        if (StrUtil.isNotEmpty(updateCustomerPar.getNickname())) {
            customerUp.setNickname(updateCustomerPar.getNickname());
        }
        if (StrUtil.isNotEmpty(updateCustomerPar.getPhone())) {
            if (!RegexUtil.checkMobile(updateCustomerPar.getPhone())) {
                throw new ParamsCheckException("手机号有误");
            }
            customerUp.setPhone(updateCustomerPar.getPhone());
        }
        if (StrUtil.isNotEmpty(updateCustomerPar.getIdcard())) {
            if (!IdcardUtil.isValidCard(updateCustomerPar.getIdcard())) {
                throw new ParamsCheckException("身份证有误");
            }
            customerUp.setIdcard(updateCustomerPar.getIdcard());
        }
        customerUp.setUpdateTime(new Date());
        int i = customerMapper.updateByPrimaryKeySelective(customerUp);

        // 资料完整度计算
        computeDataPercent(customerUp.getCustomerId());

        return i;
    }

    @Override
    public long initCustomerToken() {

        log.info("==============》初始化用户token保存在redis缓存开始 ");

        if (redisTemplate.hasKey(SunMiniappRedisConstants.CUSTOMER_CACHE_REDIS_TOKEN_KEY)) {
            // 用户存在，判断缓存token数量和数据库数量是否一样
            Long size = redisTemplate.opsForHash().size(SunMiniappRedisConstants.CUSTOMER_CACHE_REDIS_TOKEN_KEY);
            int customerTokenValidCount = customerMapper.getCustomerTokenValidCount();

            log.info(StrUtil.format("数据库有效token条数{}，redis缓存token条数{}", customerTokenValidCount, size));

            // 数量不一样，清空redis保存的token,查询数据库token缓存在redis
            if (size != customerTokenValidCount) {
                List<Map<String, Object>> customerTokenList = customerMapper.getCustomerTokenList();
                redisTemplate.delete(SunMiniappRedisConstants.CUSTOMER_CACHE_REDIS_TOKEN_KEY);
                if (CollUtil.isNotEmpty(customerTokenList)) {
                    redisTemplate.opsForHash().putAll(SunMiniappRedisConstants.CUSTOMER_CACHE_REDIS_TOKEN_KEY, listMapToMap(customerTokenList));
                    log.info("《========================清空redis保存的token,查询数据库token缓存在redis");
                } else {
                    log.info("《========================数据库没有有效token数据，清空redis中保存的token数据");
                }
            } else {
                log.info("《=========================数据库和redis保存的token数量一样，不更新redis保存的token数据(更新缓存时间)");
            }
        } else {
            // redis不存在用户token key，获取所有有效token保存在redis
            log.info("==========================》redis不存在用户token key，获取所有有效token保存在redis");
            List<Map<String, Object>> customerTokenList = customerMapper.getCustomerTokenList();
            if (CollUtil.isNotEmpty(customerTokenList)) {
                redisTemplate.opsForHash().putAll(SunMiniappRedisConstants.CUSTOMER_CACHE_REDIS_TOKEN_KEY, listMapToMap(customerTokenList));
                log.info(StrUtil.format("《========================查询数据库有效token缓存在redis，数据库有效token数{}个", customerTokenList.size()));
            } else {
                log.info("《========================数据库没有有效token数据");
            }
        }
        log.info("《==============初始化用户token保存在redis缓存结束");

        if (redisTemplate.hasKey(SunMiniappRedisConstants.CUSTOMER_CACHE_REDIS_TOKEN_KEY)) {
            // key存在,设置过期时间为x天
            redisTemplate.expire(SunMiniappRedisConstants.CUSTOMER_CACHE_REDIS_TOKEN_KEY, SunMiniappRedisConstants.CUSTOMER_CACHE_REDIS_TOKEN_KEY_VALID_DAY, TimeUnit.DAYS);
            log.info(StrUtil.format("《======================key存在,设置过期时间{}天", SunMiniappRedisConstants.CUSTOMER_CACHE_REDIS_TOKEN_KEY_VALID_DAY));
            return redisTemplate.opsForHash().size(SunMiniappRedisConstants.CUSTOMER_CACHE_REDIS_TOKEN_KEY);
        } else {
            log.info("《===========================redis不存在缓存token数据");
            return 0;
        }
    }

    private Map<String, String> listMapToMap(List<Map<String, Object>> listMap) {

        Map<String, String> resultMap = new HashMap<>();
        String s = DateUtil.formatDateTime(new Date());
        for (Map<String, Object> map : listMap) {
            resultMap.put(String.valueOf(map.get("customerToken")), StrUtil.format("{}_{}", String.valueOf(map.get("customerId")), s));
        }

        return resultMap;
    }

    @Override
    public Customer getCustomerByOpenId(String openId) {

        if (StrUtil.isEmpty(openId)) {
            return null;
        }

        CustomerExample customerExa = new CustomerExample();
        CustomerExample.Criteria customerExaCriteria = customerExa.createCriteria();
        customerExaCriteria.andOpenIdEqualTo(openId);
        List<Customer> customerList = customerMapper.selectByExample(customerExa);
        if (CollUtil.isNotEmpty(customerList)) {
            return customerList.get(0);
        }

        return null;
    }

    /**
     * 资料完整度计算,计算后直接更新数据库资料完整度
     * 计算资料包括手机号，头像，昵称，真实姓名，身份证，家庭住址，已经填写的资料个数/6 x 100%，不需要保留小数点
     * 30.3%为 30%
     *
     * @param customerId
     * @return
     */
    @Override
    public String computeDataPercent(int customerId) {

        Customer customer = customerMapper.selectByPrimaryKey(customerId);
        if (customer == null) {
            return "0%";
        }

        int dataFlag = 0;
        if (StrUtil.isNotEmpty(customer.getHeadImg())) {
            dataFlag += 1;
        }
        if (StrUtil.isNotEmpty(customer.getName())) {
            dataFlag += 1;
        }
        if (StrUtil.isNotEmpty(customer.getNickname())) {
            dataFlag += 1;
        }
        if (StrUtil.isNotEmpty(customer.getPhone())) {
            dataFlag += 1;
        }
        if (StrUtil.isNotEmpty(customer.getIdcard())) {
            dataFlag += 1;
        }

        // 是否完善地址
        CustomerAddressExample addressExam = new CustomerAddressExample();
        CustomerAddressExample.Criteria addressExamCriteria = addressExam.createCriteria();
        addressExamCriteria.andCustomerIdEqualTo(customerId);
        addressExamCriteria.andAddressStatusEqualTo(1);
        List<CustomerAddress> customerAddresses = customerAddressMapper.selectByExample(addressExam);
        if (CollUtil.isNotEmpty(customerAddresses)) {
            dataFlag += 1;
        }

        String data_percent = "0%";
        if (dataFlag > 0) {
            double i = dataFlag / 6d;
            String format = StrUtil.format("{}", i * 100);
            String sub = StrUtil.sub(format, 0, format.indexOf("."));
            data_percent = sub + "%";
        }

        // 更新
        Customer customerUp = new Customer();
        customerUp.setCustomerId(customer.getCustomerId());
        customerUp.setDataPercent(data_percent);
        customerMapper.updateByPrimaryKeySelective(customerUp);

        return data_percent;
    }

    @Override
    public CustomerDetailsVo getCustomerDetails(Integer customerId) {

        Customer customer = customerMapper.selectByPrimaryKey(customerId);
        if (customer == null) {
            throw new ParamsCheckException(ResultCode.TOKEN_ERROR.status(), "用户不存在");
        }
        CustomerDetailsVo detailsVo = new CustomerDetailsVo();
        BeanUtil.copyProperties(customer, detailsVo);

        return detailsVo;
    }

    @Override
    public AddressDetailsVo getAddressDetails(Integer addressId) {

        CustomerAddress customerAddress = customerAddressMapper.selectByPrimaryKey(addressId);
        if (customerAddress == null) {
            throw new ParamsCheckException("地址不存在");
        }
        AddressDetailsVo addressDetailsVo = new AddressDetailsVo();
        BeanUtil.copyProperties(customerAddress, addressDetailsVo);

        return addressDetailsVo;
    }

    @Override
    public List<AddressListVo> getAddressList(Integer customerId) {

        CustomerAddressExample addExam = new CustomerAddressExample();
        CustomerAddressExample.Criteria addExamCriteria = addExam.createCriteria();
        addExamCriteria.andAddressStatusEqualTo(1);
        addExamCriteria.andCustomerIdEqualTo(customerId);
        addExam.setOrderByClause("is_default desc");
        List<CustomerAddress> addressList = customerAddressMapper.selectByExample(addExam);

        List<AddressListVo> addressListVoList = new ArrayList<>();
        addressList.stream().forEach(a -> {
            AddressListVo vo = new AddressListVo();
            BeanUtil.copyProperties(a, vo);
            addressListVoList.add(vo);
        });
        return addressListVoList;
    }

    @Override
    @Transactional
    public int updateAddress(AddressAddOrUpdatePar addressAddOrUpdatePar) {

        if (StrUtil.isNotEmpty(addressAddOrUpdatePar.getPhone()) && !RegexUtil.checkMobile(addressAddOrUpdatePar.getPhone())) {
            throw new ParamsCheckException("手机号有误");
        }

        CustomerAddress addressUp = new CustomerAddress();
        BeanUtil.copyProperties(addressAddOrUpdatePar, addressUp);
        addressUp.setUpdateTime(new Date());

        if (addressAddOrUpdatePar.getIsDefault() == 1) {
            // 新增地址是默认地址，修改该用户之前所有地址为非默认
            customerAddressMapper.updateAddressNoDefault(addressAddOrUpdatePar.getCustomerId());
        }
        int i = customerAddressMapper.updateByPrimaryKeySelective(addressUp);

        return i;
    }

    @Override
    @Transactional
    public int addAddress(AddressAddOrUpdatePar addressAddOrUpdatePar) {

        if (!RegexUtil.checkMobile(addressAddOrUpdatePar.getPhone())) {
            throw new ParamsCheckException("手机号有误");
        }

        CustomerAddress addressMod = new CustomerAddress();
        BeanUtil.copyProperties(addressAddOrUpdatePar, addressMod);
        addressMod.setCreateTime(new Date());

        if (addressAddOrUpdatePar.getIsDefault() == 1) {
            // 新增地址是默认地址，修改该用户之前所有地址为非默认
            customerAddressMapper.updateAddressNoDefault(addressAddOrUpdatePar.getCustomerId());
        }

        customerAddressMapper.insertSelective(addressMod);

        // 资料完整度计算
        computeDataPercent(addressAddOrUpdatePar.getCustomerId());

        return addressMod.getAddressId();
    }
}
